﻿using Ionic.Zip;
using SharpEpub;
using SharpEpub.XmlBuilder;
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using XmlElement = SharpEpub.XmlBuilder.XmlElement;

namespace Pdf2epub.Epub
{
    public class EpubMetadata : AbstractFile
    {
        #region Public members

        private string m_Date;

        public bool CoverPageCreated { get; set; }

        public string Creator { get; set; }

        public string Date
        {
            get { return m_Date; }
            set
            {
                if (string.IsNullOrEmpty(value)) return;
                DateTime dateTime;
                if (DateTime.TryParse(value, out dateTime))
                {
                    m_Date = dateTime.ToString("yyyy-MM-dd");
                }
            }
        }

        public string Description { get; set; }

        public string Language { get; set; }

        public string Publisher { get; set; }

        public string Rights { get; set; }

        public string Source { get; set; }

        public string Subject { get; set; }

        public string Title { get; set; }

        #endregion Public members

        #region Constructors

        public EpubMetadata(EpubStructure structure, ZipFile file, string bookId)
            : base(structure, file)
        {
            bookGuid = bookId;
        }

        #endregion Constructors

        #region Overrides of AbstractFile

        protected override void Build()
        {
            contentItems = new List<string>();
            xmlDocument = new XmlDocument();
            XmlBuilder document = xmlDocument.CreateXmlBuilder("package", new Dictionary<string, string>
			                                                          	{
			                                                          		{"xmlns", Resources.OpfPackageNs},
			                                                          		{"version", "2.0"},
			                                                          		{"unique-identifier", Resources.OpfIdentifierField}
			                                                          	});
            document.AppendElement(BuildMetadataSection());
            document.AppendElement(BuildManifestSection());
            document.AppendElement(BuildSpineSection());

            if (CoverPageCreated)
            {
                document.AppendElement(BuildGuideSection());
            }
            xmlDocument = document.GetDocument();
        }

        #endregion Overrides of AbstractFile

        #region HelperFunctions

        private XmlElement BuildGuideSection()
        {
            XmlElement guideSection = xmlDocument.CreateXmlElement("guide");

            Dictionary<string, string> attributes = new Dictionary<string, string>();
            attributes["href"] = "cover.xhtml";
            attributes["title"] = "Cover";
            attributes["type"] = "cover";

            guideSection.AppendElement("reference", attributes);

            return guideSection;
        }

        private XmlElement BuildManifestSection()
        {
            XmlElement manifestSection = xmlDocument.CreateXmlElement("manifest");

            //adding ncx file link
            Dictionary<string, string> attributes = new Dictionary<string, string>();
            attributes["id"] = "ncx";
            attributes["href"] = structure.Files.NcxFile;
            attributes["media-type"] = Resources.NcxMediaType;
            manifestSection.AppendElement("item", attributes);

            //adding content files links
            attributes["media-type"] = Resources.ContentMediaType;
            foreach (ZipEntry entry in file.GetContentFiles(structure.Directories.ContentFolder))
            {
                string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(entry.FileName);
                contentItems.Add(fileNameWithoutExtension);
                attributes["id"] = "cnt_" + fileNameWithoutExtension;
                attributes["href"] = entry.FileName;
                manifestSection.AppendElement("item", attributes);
            }

            //adding css files links
            attributes["media-type"] = Resources.CssMediaType;
            foreach (ZipEntry entry in file.SelectEntries("*.css", structure.Directories.CssFolderFullPath))
            {
                attributes["id"] = "style_" + Path.GetFileNameWithoutExtension(entry.FileName);
                attributes["href"] = entry.FileName;
                manifestSection.AppendElement("item", attributes);
            }

            //adding images files links
            foreach (ZipEntry entry in file.GetImageFiles(structure.Directories.ImageFolderFullPath))
            {
                attributes["id"] = "img_" + Path.GetFileNameWithoutExtension(entry.FileName);
                attributes["href"] = entry.FileName;
                attributes["media-type"] = EpubHelper.GetImageMediaType(entry.FileName);
                manifestSection.AppendElement("item", attributes);
            }

            if (CoverPageCreated)
            {
                contentItems.Add("cover");
                attributes["id"] = "cnt_cover";
                attributes["href"] = "cover.xhtml";
                attributes["media-type"] = Resources.ContentMediaType;
                manifestSection.AppendElement("item", attributes);

                attributes["id"] = "img__cover";
                attributes["href"] = "cover.jpg";
                attributes["media-type"] = "image/jpeg";
                manifestSection.AppendElement("item", attributes);
            }

            return manifestSection;
        }

        private XmlElement BuildMetadataSection()
        {
            XmlElement metadataSection = xmlDocument.CreateXmlElement("metadata", new Dictionary<string, string>
			                                                                  	{
			                                                                  		{"xmlns:opf", Resources.MetadataXmlnsOpf},
			                                                                  		{"xmlns:dc", Resources.MetadataXmlnsDc}
			                                                                  	});
            metadataSection.NamespaceUri = Resources.MetadataXmlnsDc;
            metadataSection.AttributesNamespaceUri = Resources.MetadataXmlnsOpf;
            metadataSection.AppendTextElement("dc:title", Title);
            metadataSection.AppendTextElement("dc:creator", Creator, new Dictionary<string, string>
			                                                         	{
			                                                         		{"opf:role", Resources.OpfRole},
			                                                         		{"opf:file-as", Creator}
			                                                         	});
            metadataSection.AppendTextElement("dc:publisher", Publisher);
            metadataSection.AppendTextElement("dc:date", Date, new Dictionary<string, string>
			                                                   	{
			                                                   		{"opf:event", Resources.OpfEvent}
			                                                   	});
            metadataSection.AppendTextElement("dc:subject", Subject);
            metadataSection.AppendTextElement("dc:source", Source);
            metadataSection.AppendTextElement("dc:rights", Rights);
            metadataSection.AttributesNamespaceUri = null;
            metadataSection.AppendTextElement("dc:identifier", string.Concat(Resources.IdentifierPrefix, bookGuid),
                                              new Dictionary<string, string>
			                                  	{
			                                  		{"id", Resources.OpfIdentifierField}
			                                  	});
            metadataSection.AppendTextElement("dc:language", Language);
            metadataSection.AppendTextElement("dc:description", Description);
            return metadataSection;
        }

        private XmlElement BuildSpineSection()
        {
            Dictionary<string, string> attributes = new Dictionary<string, string>();
            attributes["toc"] = "ncx";
            XmlElement spineSection = xmlDocument.CreateXmlElement("spine", attributes);
            attributes.Clear();
            if (contentItems.Count > 0)
            {
                attributes["linear"] = "yes";
                foreach (string item in contentItems)
                {
                    attributes["idref"] = "cnt_" + item;
                    spineSection.AppendElement("itemref", attributes);
                }
            }
            return spineSection;
        }

        #endregion HelperFunctions
    }
}